﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Windows;
using Snowdreamist;
using Snowdreamist.Collection;
using Snowdreamist.ComponentModel.Plugin;
using Snowdreamist.ComponentModel.Primitives;
using Snowdreamist.Runtime;
using Snowdreamist.Runtime.Services;
using Snowdreamist.Windows.Controls;

namespace Snowdreamist.ComponentModel.Services
{
    public class DataTemplateManager : IDataTemplateManager
    {
        public DataTemplateManager() { }

        private readonly static ImportedPluginProperty<IDataTemplateFactory, IDataTemplateMetadata> DataTemplatesPluginProperty
            = ImportedPluginProperty.Create<IDataTemplateFactory, IDataTemplateMetadata>(
                new ContractMetadata(
                    new Guid("9E214551-4127-4ED6-B20E-410677D85105"),
                    null,
                    typeof(IDataTemplateFactory),
                    CandidateMode.ZeroOrMore,
                    typeof(DataTemplateManager))
                );

        public IEnumerable<ImportedPlugin<IDataTemplateFactory, IDataTemplateMetadata>> DataTemplatas
        {
            get
            {
                return DataTemplatesPluginProperty.GetValuesWithMetadata(this);
            }
        }

        public virtual void Initialize()
        {
            // get all exported data template
            if (this.DataTemplatas != null)
            {
                foreach (var dt in this.DataTemplatas)
                    try
                    {
                        this.Add(dt.Metadata.TargetTypeName, dt.Metadata.Scope, dt.Metadata.ViewState, dt.Plugin);
                    }
                    catch (ValueExistsException)
                    {
                        SystemRuntime.Current.Log.Write(TraceEventType.Warning, 0, "DataTemplateManager detects exported conflict for type " + dt.Metadata.TargetTypeName + "@Scope: " + dt.Metadata.Scope + "@viewState: " + dt.Metadata.ViewState);
                    }
                    catch (Exception error)
                    {
                        SystemRuntime.Current.Log.Write(TraceEventType.Warning, 0, "DataTemplateManager detects error when importing" + error.ToString());
                    }
            }
        }

        public DataTemplate GetTemplate(Type dataType)
        {
            if (dataType == null)
                throw new ArgumentNullException("dataType");
            return this.GetTemplate(dataType.FullName, null, null);
        }

        public DataTemplate GetTemplate(Type dataType, string scope)
        {
            if (dataType == null)
                throw new ArgumentNullException("dataType");
            return this.GetTemplate(dataType.FullName, scope, null);
        }

        public DataTemplate GetTemplate(Type dataType, string scope, string viewState)
        {
            if (dataType == null)
                throw new ArgumentNullException("dataType");
            return this.GetTemplate(dataType.FullName, scope, viewState);
        }

        public DataTemplate GetTemplate(string dataTypeName)
        {
            if (string.IsNullOrWhiteSpace(dataTypeName))
                throw new ArgumentException("Invalid dataTypeName");
            return this.GetTemplate(dataTypeName, null, null);
        }

        public DataTemplate GetTemplate(string dataTypeName, string scope)
        {
            if (string.IsNullOrWhiteSpace(dataTypeName))
                throw new ArgumentException("Invalid dataTypeName");
            return this.GetTemplate(dataTypeName, scope, null);
        }

        public DataTemplate GetTemplate(string dataTypeName, string scope, string viewState)
        {
            if (string.IsNullOrWhiteSpace("dataTypeName"))
                throw new ArgumentException("Invalid dataTypeName");

            if (viewState == null)
                viewState = string.Empty;

            List<DataTemplateWrapper> lst = null;

            if (this._dataTemplates.TryGetValue(dataTypeName, out lst))
            {
                if (lst == null)
                    return null;

                var wrapper = lst.Where(p => p.IsScopeMatch(scope) && p.IsViewStateMatch(viewState)).FirstOrDefault();

                if (wrapper == null || wrapper.Factory == null)
                    return null;
                else
                    return wrapper.Factory.Create(dataTypeName, scope, viewState);
            }
            else
            {
                return null;
            }
        }

        public virtual void OnRuntimeDisposing() { }

        public void Add(Type dataType, IDataTemplateFactory factory)
        {
            if (dataType == null)
                throw new ArgumentNullException("dataType");
            if (factory == null)
                throw new ArgumentNullException("factory");
            this.Add(dataType.FullName, factory);
        }

        public void Add(Type dataType, string scope, IDataTemplateFactory factory)
        {
            if (dataType == null)
                throw new ArgumentNullException("dataType");
            if (factory == null)
                throw new ArgumentNullException("factory");
            this.Add(dataType.FullName, scope, factory);
        }

        public void Add(Type dataType, string scope, string viewState, IDataTemplateFactory factory)
        {
            if (dataType == null)
                throw new ArgumentNullException("dataType");
            if (factory == null)
                throw new ArgumentNullException("factory");
            this.Add(dataType.FullName, scope, viewState, factory);
        }

        public void Add(string dataTypename, IDataTemplateFactory factory)
        {
            if (string.IsNullOrWhiteSpace(dataTypename))
                throw new ArgumentException("Invalid dataTypename");
            if (factory == null)
                throw new ArgumentNullException("factory");
            this.Add(dataTypename, null, null, factory);
        }

        public void Add(string dataTypename, string viewState, IDataTemplateFactory factory)
        {
            if (string.IsNullOrWhiteSpace(dataTypename))
                throw new ArgumentException("Invalid dataTypename");
            if (factory == null)
                throw new ArgumentNullException("factory");
            this.Add(dataTypename, null, viewState, factory);
        }

        public void Add(string dataTypename, string scope, string viewState, IDataTemplateFactory factory)
        {
            if (string.IsNullOrWhiteSpace(dataTypename))
                throw new ArgumentException("Invalid dataTypename");
            if (factory == null)
                throw new ArgumentNullException("factory");

            var lst = this._dataTemplates.GetOrAdd(dataTypename, () => new List<DataTemplateWrapper>());

            if (lst == null)
                throw new InvalidOperationException("Null list");

            lock (lst)
            {
                if (lst.Where(p => p.IsScopeMatch(scope) && p.IsViewStateMatch(viewState)).Count() != 0)
                    throw new ValueExistsException();
                lst.Add(new DataTemplateWrapper(scope, viewState, factory));
            }
        }

        #region private

        private readonly ThreadSafeDictionary<string, List<DataTemplateWrapper>> _dataTemplates = new ThreadSafeDictionary<string, List<DataTemplateWrapper>>();

        #endregion private
    }
}